Skip to content

AB#3312: collapse five Ensure* verbs behind BranchEnsurer#518

Merged
PolyphonyRequiem merged 1 commit into
mainfrom
arch/branch-ensurer
May 25, 2026
Merged

AB#3312: collapse five Ensure* verbs behind BranchEnsurer#518
PolyphonyRequiem merged 1 commit into
mainfrom
arch/branch-ensurer

Conversation

@PolyphonyRequiem

Copy link
Copy Markdown
Owner

Summary

Collapse the duplicated idempotent-branch-ensure matrix from the five BranchCommands.Ensure* verbs (Feature / Plan / Impl / MergeGroup / EvidenceBranch) behind a single shared Polyphony.Branching.BranchEnsurer. Pure refactor — JSON envelope unchanged on every verb; artifacts/verb-output-schemas.json diff vs. the locked fixture is empty.

Parent: AB#3311 (2026-05-24 architecture review, item #2Five Ensure verbs duplicate idempotent-ensure matrix).

What moved

From To
BranchCommands.Ensure{Feature,Plan,Impl,MergeGroup,EvidenceBranch}.cs — duplicated 8-arm matrix BranchEnsurer.EnsureAsync(BranchSpec)
BranchCommands.csTryGetCurrentBranchAsync, TryGetBranchShaAsync BranchEnsurer (the latter exposed; consumed by verbs to populate Payload.Sha)
BranchCommands.EnsureMergeGroup.csBaseExistsOnRemoteAsync BranchEnsurer (private)
BranchCommands.EnsureFeature.csBranchInOtherWorktreeRegex BranchEnsurer

Each Ensure* verb still owns:

  • input validation
  • per-kind branch-name derivation (e.g. plan/{root}-{child} vs impl/{branchId} vs mg/...)
  • per-kind base-missing-on-remote error hint (the hint depends on per-kind context the ensurer has no business knowing)

…and delegates the matrix to the ensurer. The verb projects BranchEnsureOutcome into its own kind-specific Result + Payload.

Design highlights (rubber-duck adopted)

  • BranchSpec record: Target, Base? (string?), Remote, TolerateWorktreeConflict, IncludeBaseOnRemoteCheck.
  • BranchEnsureOutcome uses private ctor + static factories (Success(...) / BaseMissing(...)) so invalid-state combinations are unrepresentable.
  • Single flag name IncludeBaseOnRemoteCheck because it gates both the base-missing-on-remote validation and the base_remote_existed payload field. Feature (no base) sets it to false and cleanly opts out of both.
  • Worktree-conflict tolerance preserved as feature-only — no behaviour change in other kinds.
  • Preserved the extra ls-remote call (for base_remote_existed reporting) even when the target branch already exists, matching current observable behaviour.

Visibility

BranchCommands is public (ConsoleAppFramework verb discovery), so BranchEnsurer + BranchSpec + BranchEnsureOutcome + BranchEnsureStatus had to be public too (initial internal attempt hit CS0051). They live in Polyphony.Branching.

Test changes

  • NEW tests/Polyphony.Tests/Branching/BranchEnsurerTests.cs — 6 focused matrix tests, including the rubber-duck-pinned cases:
    • tolerance ON + worktree-conflict + remote absent → still pushes, wasMutated=true
    • tolerance OFF + worktree-conflict → ExternalToolException propagates
  • 14 existing test fixtures updated to construct new Polyphony.Branching.BranchEnsurer(git) as the 11th BranchCommands ctor arg (bulk + a few multi-line manual edits).

Validation

  • dotnet build src/Polyphony/Polyphony.csproj -c Release
  • dotnet build tests/Polyphony.Tests/Polyphony.Tests.csproj -c Release
  • dotnet test tests/Polyphony.Tests/Polyphony.Tests.csproj -c Release --no-build4012 / 4012 PASS
  • dotnet build src/Polyphony.SchemaExporter/Polyphony.SchemaExporter.csproj -c Release regenerates artifacts/verb-output-schemas.json; diff vs. tests/lint/fixtures/verb-output-schemas.json is empty (pure refactor confirmed at the JSON contract surface).
  • Vocabulary lint: 0 violations across 838 files.
  • Type-agnosticism lint: 0 violations across 58 files.

Files

  • NEW src/Polyphony/Branching/BranchEnsurer.cs
  • NEW tests/Polyphony.Tests/Branching/BranchEnsurerTests.cs
  • Modified: src/Polyphony/Commands/BranchCommands.cs + the five Ensure* partials
  • Modified: src/Polyphony/Infrastructure/PolyphonyServiceRegistration.cs (DI registration)
  • Modified: 14 test fixtures under tests/Polyphony.Tests/Commands/ + tests/Polyphony.Tests/Journal/JournalE2ETests.cs

Out of scope

  • AB#3313 (typed-async sweep) — follow-up.
  • AB#3314 (PrLifecycle) — follow-up, depends on AB#3313.
  • AB#3315 (router-script duplication audit) — speculative, investigate next.

Extract the duplicated idempotent-branch-ensure matrix from
BranchCommands.{EnsureFeature, EnsurePlan, EnsureImpl, EnsureMergeGroup,
EnsureEvidenceBranch} into a single shared Polyphony.Branching.BranchEnsurer.
Each Ensure* verb is now a thin shell that owns:

  - input validation
  - per-kind branch-name derivation
  - per-kind `base-missing-on-remote` error hint

and delegates the (local-existed) x (remote-existed) x (base-existed) matrix
to the ensurer. Behaviour is preserved bit-for-bit at the JSON contract
surface; `artifacts/verb-output-schemas.json` diff vs. the locked fixture
is empty.

Design highlights (rubber-duck adopted):
  - `BranchSpec` record carries Target, optional Base, Remote,
    TolerateWorktreeConflict, IncludeBaseOnRemoteCheck.
  - `BranchEnsureOutcome` uses private ctor + static factories
    `Success(...)` / `BaseMissing(...)` so invalid states are
    unrepresentable.
  - Worktree-conflict tolerance preserved as feature-only (no behaviour
    change in other kinds).
  - `IncludeBaseOnRemoteCheck` gates BOTH the base-missing-on-remote
    validation AND the `base_remote_existed` payload field, so Feature
    (which has no base validation) cleanly opts out of both.

Helpers consolidated in BranchEnsurer:
  - `TryGetCurrentBranchAsync`, `TryGetBranchShaAsync` moved from
    BranchCommands.cs.
  - `BaseExistsOnRemoteAsync` moved from EnsureMergeGroup.
  - `BranchInOtherWorktreeRegex` moved from EnsureFeature.

Tests:
  - 6 new direct matrix tests in
    tests/Polyphony.Tests/Branching/BranchEnsurerTests.cs, including the
    rubber-duck-pinned cases for tolerance ON + remote-absent (still
    pushes, wasMutated=true) and tolerance OFF + conflict (propagates).
  - 14 existing test fixtures updated to construct
    `new Polyphony.Branching.BranchEnsurer(git)` as the 11th
    BranchCommands ctor arg.

Validation:
  - dotnet build (src + tests) clean.
  - dotnet test full suite: 4012/4012 PASS.
  - artifacts/verb-output-schemas.json diff vs. tests/lint/fixtures/...
    is empty (pure refactor).
  - Vocabulary + type-agnosticism lints clean.

Parent: AB#3311 (2026-05-24 architecture review).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PolyphonyRequiem PolyphonyRequiem merged commit 4dc06e7 into main May 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant